package org.abox.tools.ant.taskdefs.mks.common;

import java.io.IOException;

import org.abox.tools.ant.taskdefs.mks.util.VersionInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.types.DataType;

import com.mks.api.IntegrationPoint;
import com.mks.api.IntegrationPointFactory;
import com.mks.api.Session;
import com.mks.api.response.APIException;

/**
 * <p>
 * This class represents the Integration Point to a server.
 * It also contains a Session object.
 * </p>
 *
 * <p>
 * The framework provides two possible way to connect to the sever:
 *
 * <li><b>Remote connection</b> allows to use limited functionality only but doesn't
 * require MKS Integrity Client instance on the local environment.
 * MKS Integrity Server have to be configured to enabled API connections.
 * The plug-in does not use the MKS Integrity Client in this mode and uses
 * the API exclusively to communicate with the MKS Integrity Server.
 *
 * <li><b>Local connection</b> allows to use full-feature functionality, such as creating
 * and using a <b>Sandbox</b>. The local connection being used by default, if 'type' parameter
 * are not specified.
 *
 * </p>
 * @author Oleksandr_Korobka
 *
 */
public class Connection extends DataType implements BuildListener{

	private static final Log LOG = LogFactory.getLog(Connection.class);

	// Class variables used to create an API Session
	private String hostName;
	private int port;
	private String userName;
	private String password;
	private boolean secure;

	// The type of connection to MKS Sever: Local or Remote.
	private final String type = Type.LOCAL.name();

	// API Specific Objects
	private IntegrationPoint ip;
	private Session session;

	/**
	 * Creates the API Session Object
	 * @param secure
	 * @throws APIException
	 */
	public Session createSession() throws APIException
	{
		// Initialize our termination flag...
		ip = createIntegrityPoint();

		// Create the Session
		Session session = ip.createSession(getUserName(), password);
		// Test the connection to the MKS Integrity Server
		apiPing(session);
		LOG.info("Successfully established connection " + getUserName() + "@" + hostName + ":" + port);

		return session;
	}

	public void apiPing(Session session) {

		APIPingTask pingTask = new APIPingTask();
		pingTask.setProject(getProject());
		pingTask.setConnection(this);
		pingTask.execute();
	}

	public Session getSession() throws APIException {
		if (session == null) {
			session = createSession();
		}
		return session;
	}

	private void closeSession() {
		try {
			if (session != null) {
				session.release();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (APIException e) {
			e.printStackTrace();
		} finally {
			session = null;
		}

		try {
			if (ip != null) {
				ip.release();
			}
		} finally {
			ip = null;
		}
	}


	/**
	 * Creates the MKS Server IntegrationPoint
	 *
	 * @throws APIException
	 *
	 * @return a IntegrationPoint instance.
	 */
	protected IntegrationPoint createIntegrityPoint() throws APIException {
		switch (Type.valueOf(type)) {
		case LOCAL:
			// Create a Local Integration Point
			return IntegrationPointFactory.getInstance()
				.createLocalIntegrationPoint(VersionInfo.MAJOR_VERSION, VersionInfo.MINOR_VERSION);
		case REMOTE:
			// Create a Server Integration Point
			return IntegrationPointFactory.getInstance()
				.createIntegrationPoint(hostName, port, isSecure(), VersionInfo.MAJOR_VERSION, VersionInfo.MINOR_VERSION);
		}

		throw new IllegalArgumentException();
	}

	@Override
	public String toString() {
		return "Connection [hostName=" + hostName + ", port=" + port
		+ ", userName=" + getUserName() + "]";
	}

	public enum Type {
		LOCAL,
		REMOTE;

		public Type getType(String name) {
			return valueOf(name.trim().toUpperCase());
		}
	}

	public void buildStarted(BuildEvent event) {
	}

	public void buildFinished(BuildEvent event) {
		closeSession();
		getProject().removeBuildListener(this);
	}
	public void targetStarted(BuildEvent event) {
	}

	public void targetFinished(BuildEvent event) {
	}

	public void taskStarted(BuildEvent event) {
	}

	public void taskFinished(BuildEvent event) {
	}

	public void messageLogged(BuildEvent event) {
	}

	public void setSecure(boolean secure) {
		this.secure = secure;
	}

	public boolean isSecure() {
		return secure;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getUserName() {
		return userName;
	}


	public String getHostName() {
		return hostName;
	}

	public void setHostName(String hostName) {
		this.hostName = hostName;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public static Connection newConnection(String hostName, int port,
			String userName, String password, boolean secure) {

		Connection connection = new Connection();
		connection.setHostName(hostName);
		connection.setPort(port);
		connection.setUserName(userName);
		connection.setPassword(password);
		connection.setSecure(secure);
		return connection;
	}

	public static Connection copyConnection(Connection connection) {

		Connection connectionCopy = new Connection();
		connectionCopy.setHostName(connection.getHostName());
		connectionCopy.setPort(connection.getPort());
		connectionCopy.setUserName(connection.getUserName());
		connectionCopy.setPassword(connection.getPassword());
		connectionCopy.setSecure(connection.isSecure());
		return connectionCopy;
	}
}
